package asm;

import asm.aop.AopClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.TraceClassVisitor;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
 * Created by User on 2017/11/27.
 */
public class App {


    public static void main(String[] args) throws Exception {

        String className = "asm/Hello.class";
        String methodName = "print";

        InputStream resourceAsStream = App.class.getClassLoader().getResourceAsStream(className);
        System.out.println(resourceAsStream);
        ClassReader reader = new ClassReader(resourceAsStream);
        ClassWriter classWriter = createClassWriter(className);
        reader.accept(new AopClassAdapter(Opcodes.ASM5, classWriter), ClassReader.SKIP_DEBUG);
        byte[] bytes = classWriter.toByteArray();

        // 调用
        MyClassLoader myClassLoader = new MyClassLoader();
        Class<?> aClass = myClassLoader.defineClassForName("asm.Hello", bytes);
        Method method = aClass.getMethods()[0];
        Object invoke = method.invoke(aClass.newInstance());
        System.out.println(invoke);


    }

    private static void test1() throws Exception {
        byte[] codes = createVoidMethod("asm.Test1", "早起");

        FileOutputStream fileOutputStream = new FileOutputStream(new File("C:/test/Test1.class"));
        fileOutputStream.write(codes);
        fileOutputStream.close();

        Class<?> aClass = new MyClassLoader().defineClassForName("asm.Test1", codes);
        aClass.getMethods()[0].invoke(aClass.newInstance());
    }


    static ClassWriter createClassWriter(String className) {
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);

        // 使用jdk1.8  public修饰 类名为className  父类是java.lang.Object
        classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, className, null, "java/lang/Object", null);

        // 初始化一个无参构造
        MethodVisitor constructor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        constructor.visitVarInsn(Opcodes.ALOAD, 0);
        //执行父类的init初始化
        constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        //从当前方法返回void
        constructor.visitInsn(Opcodes.RETURN);
        constructor.visitMaxs(1, 1);
        constructor.visitEnd();
        return classWriter;
    }

    static byte[] createVoidMethod(String className, String message) throws Exception {
        //注意，这里需要把classname里面的.改成/，如com.asm.Test改成com/asm/Test
        ClassWriter classWriter = createClassWriter(className.replace('.', '/'));

        //创建run方法
        //()V表示函数，无参数，无返回值
        MethodVisitor runMethod = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "run", "()V", null, null);
        //先获取一个java.io.PrintStream对象
        runMethod.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        //将int, float或String型常量值从常量池中推送至栈顶  (此处将message字符串从常量池中推送至栈顶[输出的内容])
        runMethod.visitLdcInsn(message);
        //执行println方法（执行的是参数为字符串，无返回值的println函数）
        runMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        runMethod.visitInsn(Opcodes.RETURN);
        runMethod.visitMaxs(1, 1);
        runMethod.visitEnd();

        return classWriter.toByteArray();
    }

}
